package com.SmartCare.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.SmartCare.common.Result;
import com.SmartCare.mapper.ImageMapper;
import com.SmartCare.pojo.Image;
import com.SmartCare.service.ImageService;
import com.SmartCare.utils.ImgCompressUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class ImageServiceImpl extends ServiceImpl<ImageMapper, Image> implements ImageService {

    // 图片压缩后大小
    private static final int TARGET_SIZE = 1024 * 1024;

    @Value("${files.save.url}")
    private String fileSavePath;

    @Resource
    private ImgCompressUtil imgCompressUtil;

    @Override
    @Transactional
    public Result upload(List<MultipartFile> files, String fileUploadPath, String fileDownloadPath) {
        List<String> urls = new ArrayList<>();
        for (MultipartFile file : files) {
            // 使用hutool获取文件类型
            String originalFilename = file.getOriginalFilename();
            String type = FileUtil.extName(originalFilename);
            // 使用hutool为每个文件定义唯一的标识码
            String uuid = IdUtil.fastSimpleUUID();
            String fileUuid = uuid + StrUtil.DOT + type;
            // 新建文件，路径为设定值
            File uploadFile = new File(fileUploadPath + fileUuid);
            // 判断，如果父目录不存在就创建
            if (!uploadFile.getParentFile().exists()) {
                uploadFile.getParentFile().mkdirs();
            }

            // 压缩图片
            try {
                byte[] compressedBytes = imgCompressUtil.compressImage(file.getBytes(), type, TARGET_SIZE);
                // 保存压缩后的图片
                FileUtils.writeByteArrayToFile(uploadFile, compressedBytes);
            } catch (IOException e) {
                throw new RuntimeException("压缩图片时发生错误", e);
            }

            // 定义要封装的两个字段
            String url;
            String md5;
            // 获取文件的md5,然后根据md5先去数据库中查询文件
            md5 = SecureUtil.md5(uploadFile);
            QueryWrapper<Image> qw = new QueryWrapper<>();
            qw.eq("md5", md5);
            List<Image> images = list(qw);
            Image image;
            if (images.size() != 0) {
                image = list(qw).get(0);
            } else {
                image = null;
            }
            // 进行判断
            if (image != null) {
                // 如果已存在相同md5的文件，删除新存入的文件
                url = image.getUrl();
                uploadFile.delete();
                urls.add(url);
            } else {
                // 不存在，那就在服务器中保存
                url = fileSavePath + fileDownloadPath + "/" + fileUuid;

                // 将图片信息存入数据库
                Image dbData = new Image();
                dbData.setName(originalFilename);
                dbData.setSize((double) file.getSize() / 1024);
                dbData.setMd5(md5);
                dbData.setUrl(url);
                save(dbData);
                urls.add(url);
            }
        }
        return Result.success(urls);
    }


    @Override
    public void download(String fileUuid, String fileUploadPath, HttpServletResponse response) throws IOException {
        // 根据文件的唯一标识码来获取文件
        File uploadFile = new File(fileUploadPath + fileUuid);
        // 设置输出流的格式
        ServletOutputStream outputStream = response.getOutputStream();
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode((fileUuid), "UTF-8"));
        response.setContentType("application/octet-stream");
        // 读取文件的字节流
        outputStream.write(FileUtil.readBytes(uploadFile));
        outputStream.flush();
        outputStream.close();
    }

    /**
     * 图片打包下载代码
     */
    @Override
    public void downloads(List<String> fileUuids, String fileUploadPath, HttpServletResponse response) throws IOException {
        // 设置响应头信息，表示多个文件以压缩包形式下载
        response.addHeader("Content-Disposition", "attachment;filename=files.zip");
        response.setContentType("application/zip");
        // 获取输出流
        ServletOutputStream outputStream = response.getOutputStream();
        // 创建一个临时压缩文件
        File zipFile = File.createTempFile("files", ".zip");
        // 创建zip输出流

        try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) {
            // 遍历文件UUID列表，逐个添加到压缩包中
            for (String fileUuid : fileUuids) {
                // 根据文件的唯一标识码来获取文件
                File uploadFile = new File(fileUploadPath + fileUuid);
                // 创建一个ZipEntry对象，表示压缩包中的一个文件
                ZipEntry zipEntry = new ZipEntry(fileUuid);
                // 将ZipEntry对象添加到zip输出流
                zipOutputStream.putNextEntry(zipEntry);

                // 读取文件的字节流并写入zip输出流
                FileInputStream fileInputStream = new FileInputStream(uploadFile);
                byte[] buffer = new byte[1024];
                int len;
                while ((len = fileInputStream.read(buffer)) != -1) {
                    zipOutputStream.write(buffer, 0, len);
                }
                // 关闭当前Entry
                zipOutputStream.closeEntry();
                fileInputStream.close();
            }
        }
        // 关闭zip输出流

        // 将压缩包文件写入输出流
        try (FileInputStream zipInputStream = new FileInputStream(zipFile)) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = zipInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
        }

        // 刷新缓冲区
        outputStream.flush();
        // 关闭流
        outputStream.close();

        // 删除临时压缩文件
        zipFile.delete();
    }

    @Override
    @Transactional
    public List<String> delete(List<String> fileUuids, String fileUploadPath,String fileSavePath) {
        List<String> failedToDelete = new ArrayList<>();

        // 构建批量删除记录的方法，根据UUID模糊匹配
        QueryWrapper<Image> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("url", fileUuids.stream().map(uuid -> fileSavePath + uuid).collect(Collectors.toList()));
        remove(queryWrapper);

        // 删除文件
        for (String fileUuid : fileUuids) {
            String path = fileUploadPath + fileUuid;
            File file = new File(path);
            if (file.exists() && !file.delete()) {
                // 文件存在，但是删除失败，将其添加到失败列表中
                failedToDelete.add(fileUuid);
                // 回滚数据库操作
                throw new RuntimeException("文件删除失败: " + path);
            }
            // 如果文件不存在，认为删除成功，不做任何操作
        }
        // 如果有删除失败的文件，抛出异常以触发事务回滚
        if (!failedToDelete.isEmpty()) {
            throw new RuntimeException("部分文件删除失败: " + failedToDelete);
        }
        // 如果failedToDelete为空，意味着所有文件都被认为删除成功，返回空列表
        return failedToDelete;
    }

}
